// This file is part of TagSoup and is Copyright 2002-2008 by John Cowan.
//
// TagSoup is licensed under the Apache License,
// Version 2.0.  You may obtain a copy of this license at
// http://www.apache.org/licenses/LICENSE-2.0 .  You may also have
// additional legal rights not granted by this license.
//
// TagSoup is distributed in the hope that it will be useful, but
// unless required by applicable law or agreed to in writing, TagSoup
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, either express or implied; not even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

package org.ccil.cowan.tagsoup;

/**
The internal representation of an actual element (not an element type).
An Element has an element type, attributes, and a successor Element
for use in constructing stacks and queues of Elements.
@see ElementType
@see AttributesImpl
*/
public class Element {


	private ElementType theType;		// type of element
	private AttributesImpl theAtts;		// attributes of element
	private Element theNext;		// successor of element
	private boolean preclosed;		// this element has been preclosed

	/**
	Return an Element from a specified ElementType.
	@param type The element type of the newly constructed element
	@param defaultAttributes True if default attributes are wanted
	*/

	public Element(ElementType type, boolean defaultAttributes) {
		theType = type;
		if (defaultAttributes) theAtts = new AttributesImpl(type.atts());
		else theAtts = new AttributesImpl();
		theNext = null;
		preclosed = false;
		}

	/**
	Return the element type.
	@return The element type.
	*/

	public ElementType type() { return theType; }

	/**
	Return the attributes as an AttributesImpl object.
	Returning an AttributesImpl makes the attributes mutable.
	@return The attributes
	@see AttributesImpl
	*/
	public AttributesImpl atts() { return theAtts; }

	/**
	Return the next element in an element stack or queue.
	@return The next element
	*/

	public Element next() { return theNext; }

	/**
	Change the next element in an element stack or queue.
	@param next The new next element
	*/

	public void setNext(Element next) { theNext = next; }

	/**
	Return the name of the element's type.
	Convenience method.
	@return The element type name
	*/

	public String name() { return theType.name(); }

	/**
	Return the namespace name of the element's type.
	Convenience method.
	@return The element type namespace name
	*/

	public String namespace() { return theType.namespace(); }

	/**
	Return the local name of the element's type.
	Convenience method.
	@return The element type local name
	*/

	public String localName() { return theType.localName(); }

	/**
	Return the content model vector of the element's type.
	Convenience method.
	@return The content model vector
	*/

	public int model() { return theType.model(); }

	/**
	Return the member-of vector of the element's type.
	Convenience method.
	@return The member-of vector
	*/

	public int memberOf() { return theType.memberOf(); }

	/**
	Return the flags vector of the element's type.
	Convenience method.
	@return The flags vector
	*/

	public int flags() { return theType.flags(); }

	/**
	Return the parent element type of the element's type.
	Convenience method.
	@return The parent element type
	*/

	public ElementType parent() { return theType.parent(); }

	/**
	Return true if the type of this element can contain the type of
	another element.
	Convenience method.
	@param other The other element
	*/

	public boolean canContain(Element other) {
		return theType.canContain(other.theType);
		}


	/**
	Set an attribute and its value into this element.
	@param name The attribute name (Qname)
	@param type The attribute type
	@param value The attribute value
	*/

	public void setAttribute(String name, String type, String value) {
		theType.setAttribute(theAtts, name, type, value);
		}

	/**
	Make this element anonymous.
	Remove any <tt>id</tt> or <tt>name</tt> attribute present
	in the element's attributes.
	*/

	public void anonymize() {
		for (int i = theAtts.getLength() - 1; i >= 0; i--) {
			if (theAtts.getType(i).equals("ID") ||
			    theAtts.getQName(i).equals("name")) {
				theAtts.removeAttribute(i);
				}
			}
		}

	/**
	Clean the attributes of this element.
	Attributes with null name (the name was ill-formed)
	or null value (the attribute was present in the element type but
	not in this actual element) are removed.
	*/

	public void clean() {
		for (int i = theAtts.getLength() - 1; i >= 0; i--) {
			String name = theAtts.getLocalName(i);
			if (theAtts.getValue(i) == null || name == null ||
					name.length() == 0) {
				theAtts.removeAttribute(i);
				continue;
				}
			}
		}

	/**
	Force this element to preclosed status, meaning that an end-tag has
	been seen but the element cannot yet be closed for structural reasons.
	*/

	public void preclose() {
		preclosed = true;
		}

	/**
	Return true if this element has been preclosed.
	*/

	public boolean isPreclosed() {
		return preclosed;
		}

	}
